/**
 * The AudioTrack Demo by Chris J. Rock. Aug 10, 2011
 * Check out chrisjrock.net and be a king among men.
 *
 * Copyright (c) 2011 Chris J. Rock
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 **/


package net.chrisjrock.ui
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	
	import net.chrisjrock.sound.audioTrack.events.AudioTrackEvent;
	import net.chrisjrock.sound.audioTrack.mixer.SequenceTrack;
	import net.chrisjrock.sound.audioTrack.track.IAudioTrack;
	import net.chrisjrock.sound.audioTrack.track.IPlayableTrack;
	import net.chrisjrock.sound.audioTrack.track.ISequencedTrack;

	/**
	 * A quick, generic user-interface for manipulating an IAudioTrack object.
	 * @author ChrisJRock
	 */	
	public class AudioTrackUi
	{
		private var _enabled:Boolean;
		private var _name:String;
		private var _track:IAudioTrack;
		
		private var _display:Sprite;
		
		private var _buttonY:Number;
		
		private var _nameLabel:TextField;
		private var _playToggle:AudioTrackUiComponent;
		private var _stopButton:AudioTrackUiComponent;
		private var _muteToggle:AudioTrackUiComponent;
		private var _soloToggle:AudioTrackUiComponent;
		private var _volumeSlider:AudioTrackUiComponent;
		private var _scrubSlider:AudioTrackUiComponent;
		
		public function AudioTrackUi(name:String , track:IAudioTrack)
		{
			_name = name;
			_track = track;
		}
		
		/**
		 * Destructor.
		 */		
		public function destroy ():void
		{
			enabled = false;
			
			_track = null;
			
			if (_display && _display.parent)
			{
				_display.parent.removeChild(_display);
			}
			
			_display = null;
			_nameLabel = null;
			
			if (_playToggle)
			{
				_playToggle.destroy();
				_playToggle = null;
			}
			
			if (_stopButton)
			{
				_stopButton.destroy();
				_stopButton = null;
			}
			
			if (_muteToggle)
			{
				_muteToggle.destroy();
				_muteToggle = null;
			}
			
			if (_soloToggle)
			{
				_soloToggle.destroy();
				_soloToggle = null;
			}
			
			if (_volumeSlider)
			{
				_volumeSlider.destroy();
				_volumeSlider = null;
			}
			
			if (_scrubSlider)
			{
				_scrubSlider.destroy();
				_scrubSlider = null;
			}
		}
		
		/**
		 * Initialize the entire ui.
		 */		
		public function init ():void
		{
			if (!_track)
			{
				return;
			}
			
			buildDisplay();
			initDisplay();
			enabled = true;
		}
		
		/**
		 * Update the display to the status of the track.
		 */		
		public function initDisplay ():void
		{
			if (_track is IPlayableTrack)
			{
				_playToggle.enabled = true;
				_stopButton.enabled = true;
			}
			else
			{
				_playToggle.enabled = false;
				_stopButton.enabled = false;
				_playToggle.highlighted = false;
				_stopButton.highlighted = false;
			}
			
			_soloToggle.enabled = _track.mixer != null;
			_scrubSlider.enabled = _track is ISequencedTrack && !(_track is SequenceTrack);
			
			_track.addEventListener(AudioTrackEvent.AT_END , trackEndHandler , false , 0 , true);
		}
		
		private function updatePlayStopDisplay ():void
		{
			if (!(_track is IPlayableTrack))
			{
				return;
			}
			
			var playTrack:IPlayableTrack = IPlayableTrack(_track);
			_playToggle.highlighted = playTrack.playing;
			_stopButton.highlighted = !playTrack.playing && !playTrack.paused;
			
			if (_track is ISequencedTrack)
			{
				if (playTrack.playing)
				{
					_display.addEventListener(Event.ENTER_FRAME , scrubUpdateFrameHandler , false , 0 , true);
				}
				else
				{
					_display.removeEventListener(Event.ENTER_FRAME , scrubUpdateFrameHandler);
				}
			}
		}
		
		private function destroyInteractivity ():void
		{
			_display.mouseEnabled = _display.mouseChildren = false;
			
			_playToggle.button.removeEventListener(MouseEvent.CLICK , playClickHandler);
			_stopButton.button.removeEventListener(MouseEvent.CLICK , stopClickHandler);
			_muteToggle.button.removeEventListener(MouseEvent.CLICK , muteClickHandler);
			_soloToggle.button.removeEventListener(MouseEvent.CLICK , soloClickHandler);
			
			_volumeSlider.button.removeEventListener(MouseEvent.MOUSE_DOWN , volumeDownHandler);
			_display.stage.removeEventListener(MouseEvent.MOUSE_UP , volumeUpHandler);
			_display.removeEventListener(Event.ENTER_FRAME , volumeFrameHandler);
			
			_scrubSlider.button.removeEventListener(MouseEvent.MOUSE_DOWN , scrubDownHandler);
			_display.stage.removeEventListener(MouseEvent.MOUSE_UP , scrubUpHandler);
			_display.removeEventListener(Event.ENTER_FRAME , scrubMoveFrameHandler);
			
			_display.removeEventListener(Event.ENTER_FRAME , scrubUpdateFrameHandler);
		}
		
		private function initInteractivity ():void
		{
			_display.mouseEnabled = _display.mouseChildren = true;
			
			_playToggle.button.addEventListener(MouseEvent.CLICK , playClickHandler , false , 0 , true);
			_stopButton.button.addEventListener(MouseEvent.CLICK , stopClickHandler , false , 0 , true);
			_muteToggle.button.addEventListener(MouseEvent.CLICK , muteClickHandler , false , 0 , true);
			_soloToggle.button.addEventListener(MouseEvent.CLICK , soloClickHandler , false , 0 , true);
			
			_volumeSlider.button.addEventListener(MouseEvent.MOUSE_DOWN , volumeDownHandler , false , 0 , true);
			_scrubSlider.button.addEventListener(MouseEvent.MOUSE_DOWN , scrubDownHandler , false , 0 , true);
			
			updatePlayStopDisplay();
		}
		
		/**
		 * @private
		 * Initialize the display.
		 */		
		private function buildDisplay ():void
		{
			_display = new Sprite();
			
			_nameLabel = getLabel(_name);
			_display.addChild(_nameLabel);
			
			_buttonY = _nameLabel.height;
			
			_playToggle = buildUiComponent("Play" , 0x00ff00);
			_stopButton = buildUiComponent("Stop" , 0xff8000);
			_muteToggle = buildUiComponent("Mute" , 0xff0000);
			_soloToggle = buildUiComponent("Solo" , 0xffff00);
			_volumeSlider = buildUiComponent("Volume" , 0x0000ff , true , _track.volume);
			_scrubSlider = buildUiComponent("Playhead" , 0xff00ff , true);
		}
		
		private function buildUiComponent (text:String , fillColor:uint , slider:Boolean = false , slidePos:Number = NaN):AudioTrackUiComponent
		{
			var uiComponent:AudioTrackUiComponent = new AudioTrackUiComponent(text , fillColor , slider);
			uiComponent.init();
			
			if (!isNaN(slidePos))
			{
				uiComponent.setSlidePos(slidePos);
			}
			
			uiComponent.display.y = _buttonY;
			_buttonY += uiComponent.display.height - 2;
			
			_display.addChild(uiComponent.display);
			return uiComponent;
		}
		
		/**
		 * @private
		 * Generates a text label. 
		 * @param text
		 * @return 
		 */
		private function getLabel (text:String):TextField
		{
			var label:TextField = new TextField();
			label.autoSize = TextFieldAutoSize.LEFT;
			label.textColor = 0xffffff;
			label.selectable = false;
			var textFormat:TextFormat = new TextFormat();
			textFormat.bold = "bold";
			label.defaultTextFormat = textFormat;
			label.text = text;
			return label;
		}
		
		private function trackEndHandler (event:AudioTrackEvent):void
		{
			updatePlayStopDisplay();
		}
		
		private function playClickHandler (event:MouseEvent):void
		{
			if (!(_track is IPlayableTrack))
			{
				return;
			}
			
			var track:IPlayableTrack = IPlayableTrack(_track);
			if (track.playing)
			{
				track.pause();
			}
			else
			{
				track.play();
			}
			
			updatePlayStopDisplay();
		}
		
		private function stopClickHandler (event:MouseEvent):void
		{
			if (!(_track is IPlayableTrack))
			{
				return;
			}
			
			IPlayableTrack(_track).stop();
			_scrubSlider.setSlidePos(0);
			updatePlayStopDisplay();
		}
		
		private function muteClickHandler (event:MouseEvent):void
		{
			_track.mute = !_track.mute;
			_muteToggle.highlighted = _track.mute;
		}
		
		private function soloClickHandler (event:MouseEvent):void
		{
			_track.solo = !_track.solo;
			_soloToggle.highlighted = _track.solo;
		}
		
		private function volumeDownHandler (event:MouseEvent):void
		{
			_volumeSlider.button.removeEventListener(MouseEvent.MOUSE_DOWN , volumeDownHandler);
			
			_display.stage.addEventListener(MouseEvent.MOUSE_UP , volumeUpHandler , false , 0 , true);
			_display.addEventListener(Event.ENTER_FRAME , volumeFrameHandler , false , 0 , true);
			
			_volumeSlider.highlighted = true;
		}
		
		private function volumeUpHandler (event:MouseEvent = null):void
		{
			_display.stage.removeEventListener(MouseEvent.MOUSE_UP , volumeUpHandler);
			_display.removeEventListener(Event.ENTER_FRAME , volumeFrameHandler);
			
			_volumeSlider.button.addEventListener(MouseEvent.MOUSE_DOWN , volumeDownHandler , false , 0 , true);
			_volumeSlider.highlighted = false;
			
			volumeFrameHandler();
		}
		
		private function volumeFrameHandler (event:Event = null):void
		{
			var volumeSlidePos:Number = _display.mouseX / AudioTrackUiComponent.SLIDER_MAX_X;
			
			if (volumeSlidePos < 0)
			{
				volumeSlidePos = 0;
			}
			else if (volumeSlidePos > 1)
			{
				volumeSlidePos = 1;
			}
			
			_volumeSlider.setSlidePos(volumeSlidePos);
			_track.volume = volumeSlidePos;
		}
		
		private function scrubDownHandler (event:MouseEvent):void
		{
			_scrubSlider.button.removeEventListener(MouseEvent.MOUSE_DOWN , scrubDownHandler);
			
			_display.stage.addEventListener(MouseEvent.MOUSE_UP , scrubUpHandler , false , 0 , true);
			_display.addEventListener(Event.ENTER_FRAME , scrubMoveFrameHandler , false , 0 , true);
			
			_scrubSlider.highlighted = false;
		}
		
		private function scrubUpHandler (event:MouseEvent = null):void
		{
			_display.stage.removeEventListener(MouseEvent.MOUSE_UP , scrubUpHandler);
			_display.removeEventListener(Event.ENTER_FRAME , scrubMoveFrameHandler);
			
			_scrubSlider.button.addEventListener(MouseEvent.MOUSE_DOWN , scrubDownHandler , false , 0 , true);
			_scrubSlider.highlighted = false;
			
			scrubMoveFrameHandler();
		}
		
		private function scrubMoveFrameHandler (event:Event = null):void
		{
			var scrubSlidePos:Number = _display.mouseX / AudioTrackUiComponent.SLIDER_MAX_X;
			
			if (scrubSlidePos < 0)
			{
				scrubSlidePos = 0;
			}
			else if (scrubSlidePos > 1)
			{
				scrubSlidePos = 1;
			}
			
			if (_track is ISequencedTrack)
			{
				var sequencedTrack:ISequencedTrack = ISequencedTrack(_track);
				sequencedTrack.scrubTo(scrubSlidePos * sequencedTrack.duration);
			}
		}
		
		private function scrubUpdateFrameHandler (event:Event):void
		{
			if (_track is ISequencedTrack)
			{
				var sequencedTrack:ISequencedTrack = ISequencedTrack(_track);
				_scrubSlider.setSlidePos(sequencedTrack.position / sequencedTrack.duration);
			}
		}
		
		/**
		 * Returns the enabled status of the entire ui.
		 * @return 
		 */		
		public function get enabled ():Boolean
		{
			return _enabled;
		}
		
		/**
		 * @private
		 */		
		public function set enabled (value:Boolean):void
		{
			if (!_track || !_display || _enabled == value)
			{
				return;
			}
			
			_enabled = value;
			
			if (_enabled)
			{
				initInteractivity();
			}
			else
			{
				destroyInteractivity();
			}
		}
		
		/**
		 * Returns the displayed name of the ui. 
		 * @return 
		 */
		public function get name():String
		{
			return _name;
		}
		
		/**
		 * @private
		 */		
		public function set name(value:String):void
		{
			_name = value;
			
			if (_nameLabel)
			{
				_nameLabel.text = _name;
			}
		}
		
		/**
		 * Returns the IAudioTrack object controlled by the ui.
		 * @return 
		 */
		public function get track():IAudioTrack
		{
			return _track;
		}
		
		/**
		 * Returns the display of the ui.
		 * @return 
		 */
		public function get display ():Sprite
		{
			return _display;
		}


	}
}